46cea3470d4674d293c153eb1ad5b235dd4a8b56
[LED tetris] / tetris.c
1 /*
2  * TETRIS for AVR microcontrollers and cascading LED matrix based on MAX7219 driver
3  * Copyright (C) 2010 Dmitry Shalnov [interplaymedium.org]
4  * test it with "screen /dev/ttyUSB0" comand (control by kbd arrows)
5  * created for Interplaymediumâ„¢ AXON project
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22 #include <avr/io.h>
23 #include <avr/interrupt.h>
24
25 // WIRING 
26 // please change it according to your ARDUINO wiring
27 // don't forget to place pull-up resisor between RESET and +5V pins to avoid board reset during UART transfer.
28
29 #define DATAIN  3       
30 #define LOAD    5       
31 #define CLOCK   4       
32 #define BEEPER  2       
33
34 #define INUSE   1       // how many matrix you connect
35 #define STARTX  4       // horisontal position od figure strat
36 #define RAND_MAX   4
37
38 // Some macros that make the code more readable
39
40 #define output_low(port,pin) port &= ~(1<<pin)
41 #define output_high(port,pin) port |= (1<<pin)
42 #define set_input(portdir,pin) portdir &= ~(1<<pin)
43 #define set_output(portdir,pin) portdir |= (1<<pin)
44
45 #define FOSC    16000000
46 #define BAUD    9600
47 #define UBRR    FOSC/16/BAUD-1
48
49 volatile unsigned char u, oldu;
50
51 // ------------------------------------------- Set Receive Interrupt Enable ------------------------------------------- 
52
53 void setRXCIE_USART0()
54 {
55     UCSR0B |= _BV(RXCIE0);
56 }
57
58 // -------------------------------------------- Initialize USART0 -----------------------------------------------------
59
60 void init_USART0 (unsigned int baud) {
61         UBRR0 = baud;                           // Set Baudrate
62         UCSR0C = (3<<UCSZ00);                   // Character Size 8 bit
63         UCSR0B |= _BV(RXEN0) | _BV(TXEN0);      // Receiver and Transmitter Enable
64 }
65
66 // -------------------------------------------- Receive 1 byte  Data --------------------------------------------------
67
68 unsigned char receive_1byte_USART0 (void) {
69         loop_until_bit_is_set(UCSR0A, RXC0);
70         return UDR0;
71 }
72
73 // -------------------------------------------- Transmit 1 byte Data --------------------------------------------------
74
75 void transmit_1byte_USART0 (unsigned char data) {
76         loop_until_bit_is_set(UCSR0A, UDRE0);
77         UDR0 = data;
78 }
79
80 // -------------------------------------------- Interrupt catch -------------------------------------------------------
81
82 ISR(USART_RX_vect){ // USART RX interrupt
83         volatile unsigned char c;
84         c = UDR0;
85         //transmit_1byte_USART0('w');
86         //output_high(PORTB, LED1);
87         //if (c<'5' && c>'0') 
88         u = c;
89 }
90
91 // --------------------------------------------- Transmit String Data -------------------------------------------------
92
93 void transmit_str_USART0 (char *str) {
94         while (*str != 0) {
95                 transmit_1byte_USART0(*str);
96                 *str++;
97         }
98 }
99
100 // --------------------------------------------- Transmit Four-Digit Integer ------------------------------------------
101
102 void transmit_4digit_USART0 (int num) {
103         unsigned char temp;
104         int digit = 1000;
105
106         while (digit != 0) {
107                 temp = num / digit;
108                 transmit_1byte_USART0('0'+temp);
109                 num -= (digit*temp);
110                 digit /= 10;
111         }
112 }
113
114 // --------------------------------------------- Delays --------------------------------------------------------------
115
116 void delay_ms (uint16_t ms) {
117         uint16_t delay_count = FOSC / 17500 * 0.1;
118         volatile uint16_t i;
119         while (ms != 0) {
120                 for (i=0; i != delay_count; i++);
121                 ms--;
122         }
123 }
124
125 void delay_ns(uint16_t ms) {
126         uint16_t delay_count = FOSC / 17500 * 0.01;
127         volatile uint16_t i;
128         while (ms != 0) {
129                 for (i=0; i != delay_count; i++);
130                 ms--;
131         }
132 }
133
134 // --------------------------------------------- Sounds -------------------------------------------------------------
135
136 void beep (){
137         for(uint8_t a=0; a<100; a++){
138                 output_high(PORTB, BEEPER);
139                 delay_ms(1);
140                 output_low(PORTB, BEEPER);
141                 delay_ms(1);
142         }
143 }
144
145 void bleepBleepSound (){
146         uint8_t a, b;
147         for(b=1; b<40; b+=10) for(a=0; a<100; a++){
148                 output_high(PORTB, BEEPER);
149                 delay_ns(b);
150                 output_low(PORTB, BEEPER);
151                 delay_ns(b);
152         }
153 }
154
155 // --------------------------------- LED MATRIX code ---------------------------------------------------------------
156
157 // define max7219 registers
158 uint8_t max7219_reg_noop        = 0x00;
159 uint8_t max7219_reg_digit0      = 0x01;
160 uint8_t max7219_reg_digit1      = 0x02;
161 uint8_t max7219_reg_digit2      = 0x03;
162 uint8_t max7219_reg_digit3      = 0x04;
163 uint8_t max7219_reg_digit4      = 0x05;
164 uint8_t max7219_reg_digit5      = 0x06;
165 uint8_t max7219_reg_digit6      = 0x07;
166 uint8_t max7219_reg_digit7      = 0x08;
167 uint8_t max7219_reg_decodeMode  = 0x09;
168 uint8_t max7219_reg_intensity   = 0x0a;
169 uint8_t max7219_reg_scanLimit   = 0x0b;
170 uint8_t max7219_reg_shutdown    = 0x0c;
171 uint8_t max7219_reg_displayTest = 0x0f;
172
173
174 void putByte(uint8_t data) {
175         uint8_t i = 8;
176         uint8_t mask;
177         while(i > 0) {
178                 mask = 0x01 << (i - 1);      // get bitmask
179                 //digitalWrite( CLOCK, LOW);   // tick
180                 output_low(PORTB, CLOCK);       // tick
181                 if (data & mask){            // choose bit
182                         //digitalWrite(DATAIN, HIGH);// send 1
183                         output_high(PORTB, DATAIN);
184                 } else {
185                      //digitalWrite(DATAIN, LOW); // send 0
186                         output_low(PORTB, DATAIN);
187                 }
188                 //digitalWrite(CLOCK, HIGH);   // tock
189                 output_high(PORTB, CLOCK);
190                 --i;                         // move to lesser bit
191         }
192 }
193
194 void maxOne(uint8_t maxNr, uint8_t reg, uint8_t col) {    
195         //maxOne is for adressing different MAX7219's, 
196         //whilele having a couple of them cascaded
197         int c = 0;
198         //digitalWrite(load, LOW);  // begin     
199         output_low(PORTB, LOAD);
200
201         for ( c = INUSE; c > maxNr; c--) {
202                 putByte(0);    // means no operation
203                 putByte(0);    // means no operation
204         }
205
206         putByte(reg);  // specify register
207         putByte(col);//((data & 0x01) * 256) + data >> 1); // put data 
208
209         for ( c = maxNr-1; c >= 1; c--) {
210                 putByte(0);    // means no operation
211                 putByte(0);    // means no operation
212         }
213
214         //digitalWrite(load, LOW); // and load da shit
215         output_low(PORTB, LOAD);
216         //digitalWrite(load,HIGH); 
217         output_high(PORTB, LOAD);
218 }
219
220 /*
221 void putPixel (uint8_t maxNr, uint8_t x, uint8_t y){
222         //maxOne is for adressing different MAX7219's, 
223         //whilele having a couple of them cascaded
224
225         int c = 0;
226         //digitalWrite(load, LOW);  // begin     
227         output_low(PORTB, LOAD);
228
229         for ( c = INUSE; c > maxNr; c--) {
230                 putByte(0);    // means no operation
231                 putByte(0);    // means no operation
232         }
233
234         putByte(y);  // specify register
235         putByte(0x01 << (8-x));//((data & 0x01) * 256) + data >> 1); // put data 
236
237         for ( c = maxNr-1; c >= 1; c--) {
238                 putByte(0);    // means no operation
239                 putByte(0);    // means no operation
240         }
241
242         //digitalWrite(load, LOW); // and load da shit
243         output_low(PORTB, LOAD);
244         //digitalWrite(load,HIGH); 
245         output_high(PORTB, LOAD);
246 }
247 */
248
249 // --------------------------------- Figures ----------------------------------------------------------------------
250
251 uint8_t figure[5][4][4] = {     
252                                 {
253                                         {
254                                         0b00011000,
255                                         0b00011000,
256                                         0b00000000,
257                                         0b00000000
258                                         },
259                                         {
260                                         0b00011000,
261                                         0b00011000,
262                                         0b00000000,
263                                         0b00000000
264                                         },
265                                         {
266                                         0b00011000,
267                                         0b00011000,
268                                         0b00000000,
269                                         0b00000000
270                                         },
271                                         {
272                                         0b00011000,
273                                         0b00011000,
274                                         0b00000000,
275                                         0b00000000
276                                         }
277                                 },  
278                                 {
279                                         {
280                                         0b00110000,
281                                         0b00011000,
282                                         0b00000000,
283                                         0b00000000
284                                         },
285                                         {
286                                         0b00010000,
287                                         0b00110000,
288                                         0b00100000,
289                                         0b00000000
290                                         },
291                                         {
292                                         0b00110000,
293                                         0b00011000,
294                                         0b00000000,
295                                         0b00000000
296                                         },
297                                         {
298                                         0b00010000,
299                                         0b00110000,
300                                         0b00100000,
301                                         0b00000000
302                                         }
303                                 }, 
304                                 {
305                                         {
306                                         0b00000000,
307                                         0b00111000,
308                                         0b00001000,
309                                         0b00000000
310                                         },
311                                         {
312                                         0b00011000,
313                                         0b00010000,
314                                         0b00010000,
315                                         0b00000000
316                                         },
317                                         {
318                                         0b00100000,
319                                         0b00111000,
320                                         0b00000000,
321                                         0b00000000
322                                         },
323                                         {
324                                         0b00010000,
325                                         0b00010000,
326                                         0b00110000,
327                                         0b00000000
328                                         }
329                                 }, 
330                                 {
331                                         {
332                                         0b00010000,
333                                         0b00111000,
334                                         0b00000000,
335                                         0b00000000
336                                         },
337                                         {
338                                         0b00010000,
339                                         0b00110000,
340                                         0b00010000,
341                                         0b00000000
342                                         },
343                                         {
344                                         0b00000000,
345                                         0b00111000,
346                                         0b00010000,
347                                         0b00000000
348                                         },
349                                         {
350                                         0b00010000,
351                                         0b00011000,
352                                         0b00010000,
353                                         0b00000000
354                                         }
355                                 }, 
356                                 {
357                                         {
358                                         0b00000000,
359                                         0b00111100,
360                                         0b00000000,
361                                         0b00000000
362                                         },
363                                         {
364                                         0b00010000,
365                                         0b00010000,
366                                         0b00010000,
367                                         0b00010000
368                                         },
369                                         {
370                                         0b00000000,
371                                         0b00111100,
372                                         0b00000000,
373                                         0b00000000
374                                         },
375                                         {
376                                         0b00010000,
377                                         0b00010000,
378                                         0b00010000,
379                                         0b00010000
380                                         }
381                                 }
382                           };
383
384  
385 // ----------------------------------------- Init variables ------------------------------------------------------------
386
387 uint8_t screen[ INUSE*8 + 1 ];
388
389 uint8_t currentFigure = 4;
390 uint8_t currentTurn = 0;
391 uint8_t currentY = 0;
392 uint8_t currentX = STARTX;
393 uint32_t timer = 0;
394 uint8_t randomDigit = 0;
395 uint8_t prewRandomDigit;
396 uint8_t score = 1; 
397
398 // ------------------------------------------ Game logic ---------------------------------------------------------------
399
400 uint8_t moveLine(uint8_t L, uint8_t X){
401         if (X <= 4 ) L <<= 4 - X; else L >>= X - 4;
402         return L;
403 }
404
405 void redrawScreen (void) {
406         uint8_t lineToPlace = 0b00000000;
407         for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
408                 if (matrix*8 + line >= currentY && matrix*8 + line < currentY+4) lineToPlace = figure[currentFigure][currentTurn][matrix*8 +line - currentY]; else lineToPlace = 0b00000000;
409                 //lineToPlace = 0b11111111; else lineToPlace = 0b00000000;
410                 //if (currentX <= 4 ) lineToPlace <<= 4 - currentX; else lineToPlace >>= currentX - 4;
411                 lineToPlace = moveLine(lineToPlace, currentX);
412                 maxOne(matrix+1, line+1, screen[matrix*8 + line] | lineToPlace );       
413         }
414 }
415
416 uint8_t checkDown (void){
417         uint8_t lineToPlace = 0b00000000;
418         for (uint8_t line = 0; line<4; line ++){
419                 lineToPlace = figure[currentFigure][currentTurn][line];
420                 lineToPlace = moveLine(lineToPlace, currentX);
421                 if ( (lineToPlace & screen[ currentY+1 + line]) != 0) return 1;
422         }
423         return 0;
424 }
425
426 uint8_t checkStart (void){
427         uint8_t lineToPlace = 0b00000000;
428         for (uint8_t line = 0; line<4; line ++){
429                 lineToPlace = figure[currentFigure][currentTurn][line];
430                 lineToPlace = moveLine(lineToPlace, currentX);
431                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
432         }
433         return 0;
434 }
435
436 uint8_t checkLeft (void){
437         uint8_t lineToPlace = 0b00000000;
438         uint8_t tmpLineToPlace;
439         for (uint8_t line = 0; line<4; line ++){
440                 lineToPlace = figure[currentFigure][currentTurn][line];
441                 tmpLineToPlace = moveLine(lineToPlace, currentX);
442                 if ( (tmpLineToPlace & 0b10000000) != 0) return 1;
443                 lineToPlace = moveLine(lineToPlace, currentX - 1);
444                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
445         }
446         return 0;
447 }
448
449 uint8_t checkRight (void){
450         uint8_t lineToPlace = 0b00000000;
451         uint8_t tmpLineToPlace;
452         for (uint8_t line = 0; line<4; line ++){
453                 lineToPlace = figure[currentFigure][currentTurn][line];         
454                 tmpLineToPlace = moveLine(lineToPlace, currentX);
455                 if ( (tmpLineToPlace & 0b00000001) != 0) return 1;
456                 lineToPlace = moveLine(lineToPlace, currentX + 1);
457                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
458         }
459         return 0;
460 }
461
462 uint8_t checkTurn (void){
463         uint8_t lineToPlace = 0b00000000;
464         uint8_t testTurn; 
465         if (currentTurn < 3) testTurn =  currentTurn + 1; else testTurn = 0;
466         for (uint8_t line = 0; line<4; line ++){
467                 lineToPlace = figure[currentFigure][testTurn][line];
468                 lineToPlace = moveLine(lineToPlace, currentX);
469                 //if ( (lineToPlace & 0b00000001) != 0) return 1; 
470                 if (currentFigure == 1 && currentX == 8) return 1;
471                 if (currentFigure == 2 && currentX == 1) return 1;
472                 if (currentFigure == 2 && currentX == 8) return 1;
473                 if (currentFigure == 3 && currentX == 1) return 1;
474                 if (currentFigure == 3 && currentX == 8) return 1;   
475                 if (currentFigure == 4 && currentX == 2) return 1;
476                 if (currentFigure == 4 && currentX == 7) return 1; 
477                 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
478         }
479         return 0;
480 }
481
482 void checkFullLine (void) {
483         uint8_t a = 0;
484         uint8_t matrix = currentY / (INUSE*8);
485         uint8_t remainder = currentY % (INUSE*8);
486         for (uint8_t line = 0; line<4; line ++) if (line + currentY < INUSE*8) {
487                 if (screen[line + currentY] == 0b11111111) {
488                         for (a = 0; a<3; a++) {
489                                 
490                                 maxOne(matrix + 1, line + remainder +1, 0b00000000 );
491                                 //screen[line + currentY] = 0b11111111; 
492                                 //redrawScreen ();
493                                 delay_ms(1000);
494                                 maxOne(matrix + 1, line + remainder +1, 0b11111111 );   
495                                 //screen[line + currentY] = 0b00000000;
496                                 //redrawScreen ();
497                                 delay_ms(1000);
498                         }
499                         for (a = line + currentY; a>0; a--) screen[a] = screen[a-1];
500                         bleepBleepSound ();
501                         transmit_str_USART0("Score: "); 
502                         transmit_4digit_USART0(score++);
503                         transmit_str_USART0("        \r");
504                 }
505         }
506 }
507
508 void gameOver () {
509         uint8_t ornament[3] = {0b00100100, 0b10010010, 0b01001001};
510         uint8_t offset = 0;
511         for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
512                 maxOne(matrix + 1, line + 1, 0b11111111 );
513                 delay_ms(500);
514         }
515         for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
516                 maxOne(matrix + 1, line + 1, 0b00000000 );
517                 screen[ matrix*8 + line] = 0b00000000;
518                 delay_ms(500);
519         }
520
521         while (u == 0) {
522                 for (uint8_t matrix=0;  matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
523                         maxOne(matrix + 1, line + 1, ornament[(line+offset) % 3] );
524                 }
525                 if (offset < 2) offset ++; else offset = 0;
526                 delay_ms(1000);
527         }
528         
529         score = 1;
530         transmit_str_USART0("Score: 0000        \r");
531
532         currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)5);
533         currentTurn = 0;
534         
535 }
536
537 void stopFigure (void) {
538         uint8_t lineToPlace = 0b00000000;
539         for (uint8_t line = 0; line<4; line ++) {
540                 lineToPlace = figure[currentFigure][currentTurn][line];
541                 lineToPlace = moveLine(lineToPlace, currentX);
542                 screen[line + currentY] |= lineToPlace;
543         }
544         checkFullLine ();
545         currentY = 0;
546         currentTurn = 0;
547         currentX = STARTX;
548         currentFigure = randomDigit;
549
550         if (checkStart () != 1) redrawScreen (); else gameOver();
551 }
552
553 // --------------------------------- MAIN -----------------------------------------------------------------------
554
555 int main(void) {
556
557         // initialize the direction of PORTD to be output
558
559         set_output(DDRB, DATAIN);  
560         set_output(DDRB, LOAD);
561         set_output(DDRB, CLOCK);
562         set_output(DDRB, BEEPER);
563
564         // initiation of the max 7219
565         for (uint8_t matrix=0; matrix < INUSE; matrix++) {
566                 maxOne(matrix+1, max7219_reg_scanLimit, 0x07);      
567                 maxOne(matrix+1, max7219_reg_decodeMode, 0x00);                 // using a led matrix, not digits
568                 maxOne(matrix+1, max7219_reg_shutdown, 0x01);                   // not in shutdown mode
569                 maxOne(matrix+1, max7219_reg_displayTest, 0x00);                // no display test
570                 for (uint8_t line=0; line<8; line ++) maxOne(matrix+1, line+1, 0);      // empty registers, turn all LEDs off 
571                 maxOne(matrix+1, max7219_reg_intensity, 0x0f & 0x0f);           // the first 0x0f is the value you can set (range: 0x00 to 0x0f)
572         }
573                                                         
574         init_USART0(UBRR);  // initialize USART0
575         setRXCIE_USART0();
576         sei();
577
578         screen[ INUSE*8 ] = 0b11111111;                                         // bottom border for checking
579         currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);
580         transmit_str_USART0("Score: 0000        \r");
581
582         while (1) {
583                 if (timer % 20000 == 0) {
584                         redrawScreen ();
585                         if (checkDown() != 1) currentY ++; else {
586                                 if (prewRandomDigit == randomDigit) randomDigit++;
587                                 if (randomDigit > RAND_MAX+1) randomDigit = 0;
588                                 prewRandomDigit = randomDigit;
589                                 stopFigure ();
590                         }
591                 }
592                 timer ++;
593                 if (u != oldu){
594                         oldu = u;
595                         randomDigit = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);  
596                         //transmit_1byte_USART0(timer);
597                         //transmit_4digit_USART0(u);    
598                         if (67 == u) {
599                                 //transmit_str_USART0(" RIGHT \r");
600                                 if (checkRight () != 1) currentX ++;
601                                 redrawScreen ();
602                                 beep ();
603                         }
604                         if (68 == u) {
605                                 //transmit_str_USART0(" LEFT  \r");
606                                 if (checkLeft () != 1) currentX --;
607                                 redrawScreen ();
608                                 beep ();
609                         }
610                         if (66 == u) {
611                                 //transmit_str_USART0(" DOWN  \r");
612                                 if (checkDown() != 1) currentY ++; 
613                                 redrawScreen ();
614                                 beep ();
615                         }
616                         if (65 == u) {
617                                 //transmit_str_USART0(" UP    \r");
618                                 if (checkTurn () != 1) { 
619                                         if (currentTurn < 3) currentTurn ++; else currentTurn = 0;
620                                         redrawScreen ();
621                                         beep ();
622                                 }
623                         }
624                         u = 0;
625                 }
626         }
627
628 }
629
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717